package com.material.demo.record;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Build;
import android.text.TextUtils;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by WenJackp on 2016/12/13.
 * * 参考
 * https://developer.android.google.cn/guide/appendix/media-formats.html(配置)
 */

public class AudioRecordHelper extends BaseRecordHelper {

    private static final String TAG = AudioRecordHelper.class.getName();

    private AudioRecord record;

    //参考 MediaRecorder.AudioSource.MIC
    private int audioSourcre;
    //录制频率 8000hz or 11025hz 等和设备有关
    private int sampleRateInHz;
    //ChannelConfig:录制通道，可以为AudioFormat.CHANNEL_CONFIGURATION_MONO和AudioFormat.CHANNEL_CONFIGURATION_STEREO
    private int channelConfig;
    //AudioFormat:录制编码格式，可以为AudioFormat.ENCODING_16BIT和8BIT,其中16BIT的仿真性比8BIT好，但是需要消耗更多的电量和存储空间
    private int audioFormat;

    private boolean isRecording;
    private int minBufferSize;
    private boolean mStopRecord;
    private File outputFile;
    private ExecutorService service;
    private static AudioRecordHelper mHelper;

    public static AudioRecordHelper getInstance() {
        if (mHelper == null) {
            synchronized (AudioRecordHelper.class) {
                if (mHelper == null) {
                    mHelper = new AudioRecordHelper();
                }
            }
        }
        return mHelper;
    }

    public static AudioRecordHelper createNewHelper() {
        return new AudioRecordHelper();
    }

    AudioRecordHelper() {
        logMsg("init config");

        isRecording = false;
        mStopRecord = false;

        service = Executors.newSingleThreadExecutor();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            channelConfig = AudioFormat.CHANNEL_IN_MONO;
        } else {
            channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;
        }

        sampleRateInHz = DEFAULT_SAMPLE_RATE;
        audioFormat = AudioFormat.ENCODING_PCM_16BIT;
        audioSourcre = MediaRecorder.AudioSource.MIC;
    }

    public void setOutput(File outputFile) {
        this.outputFile = outputFile;
    }

    public void setOutput(String path) {
        if (!TextUtils.isEmpty(path)) {
            File file = new File(path);
            if (!file.exists()) {
                try {
                    file.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            setOutput(file);
        }
    }

    /**
     * 开始录制
     */
    public void startRecord() {

        if (isRecording) {
            logMsg("can't not record ! (isRecording:" + isRecording + ")");
            return;
        }

        sampleRateInHz = getBestSimpleRate(channelConfig, audioFormat, DEFAULT_SAMPLE_RATE, INVALID_VALUE);
        if (sampleRateInHz == INVALID_VALUE) {
            throw new RuntimeException("获取合适的采样率失败!");
        }

        if (outputFile == null || !outputFile.exists()) {
            throw new RuntimeException("output file not exists");
        }

        minBufferSize = getMinBufferSize(sampleRateInHz, channelConfig, audioFormat, MIN_BUFFER_SIZE);

        if (record == null) {
            record = createAudioRecord(audioSourcre, sampleRateInHz, channelConfig, audioFormat, minBufferSize);
        }

        record.startRecording();
        mStopRecord = false;
        isRecording = true;
        service.execute(new AudioRecordTask());
    }

    /**
     * 停止录制
     */
    public void stopRecord() {
        logMsg("StopRecord :  isRecording " + isRecording);
        if (isRecording) {
            isRecording = false;
            mStopRecord = true;
        }
    }

    /**
     * 创建一个AudioRecord对象
     *
     * @param audioSourcre      @See{ MediaRecorder.AudioSource }
     * @param sampleRateInHz    采样率这个值和设备有关通常包含 8000 11025 16000 44100;
     * @param channelConfig     Audio Channel AudioFormat.CHANNEL_IN_MONO(低音质)  AudioFormat.CHANNEL_IN_STEREO(高音质)  区分输入和输出 IN 和 OUT
     * @param audioFormat       @See{AudioFormat.ENCODING_?}
     * @param bufferSizeInBytes
     * @return
     */
    private AudioRecord createAudioRecord(int audioSourcre, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes) {
        AudioRecord record;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            record = new AudioRecord.Builder()
                    .setAudioSource(audioSourcre)
                    .setAudioFormat(new AudioFormat.Builder()
                            .setEncoding(audioFormat)
                            .setSampleRate(sampleRateInHz)
                            .setChannelMask(channelConfig)
                            .build())
                    .setBufferSizeInBytes(bufferSizeInBytes)
                    .build();
        } else {
            record = new AudioRecord(audioSourcre, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes);
        }

        logMsg("AudioSource : " + audioSourcre + "\n" +
                "SampleRateInHz : " + sampleRateInHz + "\n" +
                "ChannelConfig : " + channelConfig + "\n" +
                "AudioFormat : " + audioFormat + "\n" +
                "BufferSizeInBytes : " + bufferSizeInBytes);

        return record;
    }

    class AudioRecordTask implements Runnable {

        @Override
        public void run() {
            logMsg("StartRecord : " + mStopRecord + "   MinBuffer : " + minBufferSize);

            byte[] buffer = new byte[minBufferSize];
            FileOutputStream fos = null;
            BufferedOutputStream bos = null;
            try {
                fos = new FileOutputStream(outputFile);
                bos = new BufferedOutputStream(fos);
                while (!mStopRecord) {
                    int status = record.read(buffer, 0, buffer.length);
                    if (status > 0) {
                        bos.write(buffer, 0, status);
                    }
                }
                record.stop();
                fos.flush();
                bos.flush();
                logMsg("record compelete");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {

                if (bos != null) {
                    try {
                        bos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 释放资源
     */
    public void release() {

        stopRecord();

        if (service != null && !service.isShutdown()) {
            service.shutdown();
            service = null;
        }

        if (mSupportRates != null) {
            mSupportRates.clear();
            mSupportRates = null;
        }
    }

    @Override
    protected String getTag() {
        return TAG;
    }
}

